/*
 * Copyright (c) 2008, Xiong Yingfei, the University of Tokyo
 * All rights reserved.
 */
package ac.jp.u_tokyo.SyncLib.language2;

import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import ac.jp.u_tokyo.SyncLib.language.DictMapBody;
import ac.jp.u_tokyo.SyncLib.language.EDictMapBody;
import ac.jp.u_tokyo.SyncLib.language.EvaluationFailedException;
import ac.jp.u_tokyo.SyncLib.language.GraphCombinatorBody;
import ac.jp.u_tokyo.SyncLib.language.IntegerConst;
import ac.jp.u_tokyo.SyncLib.language.KeyDictMapBody;
import ac.jp.u_tokyo.SyncLib.language.ObjCreation;
import ac.jp.u_tokyo.SyncLib.language.SyncBody;
import ac.jp.u_tokyo.SyncLib.modParser.ModParser.dictmoditem_return;

public class ForMap extends FixedParaSyncConstructBase
{
	
	public ForMap(int line)
	{
		super(line);
	}

	private boolean _isEqualMap = true;
	private List<Binding> _varBindings = new LinkedList<Binding>();
	private List<Var> _dictionaries = new LinkedList<Var>();
	private SyncConstruct _innerSync;
	
	private static abstract class Binding {
		public abstract void addToSet(Collection<Var> vars);
		public abstract Var getValueVar();
		public abstract Var getKeyVar();
	}
	private static class ValueBinding extends Binding {
		Var valueVar;
		public void addToSet(Collection<Var> vars) {
			vars.add(valueVar);
		}
		public Var getValueVar() {
			return valueVar;
		}
		@Override
		public Var getKeyVar()
		{
			return new Var(Helper.VARGENERATOR.getNextName(), 0);
		}
		
	}
	private static class PairBinding extends Binding{
		Var keyVar;
		Var valueVar;
		public void addToSet(Collection<Var> vars) {
			vars.add(valueVar);
			vars.add(keyVar);
		}
		public Var getValueVar() {
			return valueVar;
		}
		@Override
		public Var getKeyVar()
		{
			return keyVar;
		}

	}
	
	
	public void addValueBinding(Var valueVar) {
		ValueBinding v = new ValueBinding();
		v.valueVar = valueVar;
		_varBindings.add(v);
	}
	
	public void setSyncConstruct(SyncConstruct s) {
		_innerSync = s;
	}

	public void addPairBinding(Var keyVar, Var valueVar) {
		PairBinding v = new PairBinding();
		v.valueVar = valueVar;
		v.keyVar = keyVar;
		_varBindings.add(v);
	}
	
	public void setEqualMap(boolean isEqualMap) {
		_isEqualMap = isEqualMap;
	}
	
	public void addDictionary(Var var) {
		_dictionaries.add(var);
	}


	@Override
	public Set<Var> getVarUsed(Collection<String> genericParas)
			throws EvaluationFailedException
	{
		Set<Var> result = getNonDictVars(genericParas);
		result.addAll(_dictionaries);
		return result;
	}

	private Set<Var> getNonDictVars(Collection<String> genericParas)
			throws EvaluationFailedException
	{
		Set<Var> result = new HashSet<Var>(_innerSync.getVarUsed(genericParas));
		Set<Var> bindings = new HashSet<Var>();
		for(Binding b : _varBindings) {
			b.addToSet(bindings);
		}
		for (Var v : bindings) {
			v.check(genericParas);
			result.remove(v);
		}
		return result;
	}

	@Override
	public void addBody(GraphCombinatorBody body,
			Collection<String> genericParas) throws EvaluationFailedException
	{
		SyncBody result;
		List<Var> nonDictList = new LinkedList<Var>(getNonDictVars(genericParas));
		if (_isEqualMap) {
			List<Var> paras = new LinkedList<Var>();
			for (Binding var : _varBindings) {
				if (var instanceof PairBinding) 
					throw new PairNotSupportedException(((PairBinding)var).keyVar.getLine());
				paras.add(((ValueBinding)var).valueVar);
			}
			paras.addAll(nonDictList);
//			System.out.println(paras);
			SyncBody inner = _innerSync.getBody(paras, genericParas);

			result = new EDictMapBody(getLine(), inner, new IntegerConst(getLine(), _dictionaries.size()));
		}
		else {
			KeyDictMapBody keyDictMapBody = new KeyDictMapBody(getLine());
			List<Var> paras = new LinkedList<Var>();
			
			for (Binding var : _varBindings) {
				paras.add(var.getValueVar());
			}
			for (Binding var : _varBindings) {
				paras.add(var.getKeyVar());
			}
			paras.addAll(nonDictList);
			keyDictMapBody.setInner(_innerSync.getBody(paras, genericParas));
			for (Var var : _dictionaries)
			{
				keyDictMapBody.addFactory(new ObjCreation(var.getLine(), GlobalIncrementalFactory.class.getName()));
			}
			
			result = keyDictMapBody;
		}
		
		List<Var> paras = new LinkedList<Var>();
		for (Var var : _dictionaries) {
			paras.add(var);
		}
		paras.addAll(nonDictList);
		body.addConnection(result, Helper.higherVar2LowerVar(paras));
		
	}
	
	private static class PairNotSupportedException extends EvaluationFailedException {

		/**
		 * 
		 */
		private static final long serialVersionUID = 0L;

		public PairNotSupportedException(int line)
		{
			super(line);
		}

		@Override
		protected String getInternalMessage()
		{
			return "Pair binding in equality map is not supported yet";
		}
		
	}
}
